package com.lry.sso.auth.common.util;

import cn.hutool.core.io.IoUtil;
import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.*;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by ace on 2017/9/10.
 */
public class RsaKeyHelper {

//    private static final Logger logger = LoggerFactory.getLogger(RsaKeyHelper.class);

    private static final String KEY_ALGORITHM = "RSA";
    private static final int MAX_ENCRYPT_1024 = 117;
    private static final int MAX_DECRYPT_1024 = 128;
    /**
     * 从文件证书获取公钥
     *
     * @param filename 文件名称
     * @return 公钥对象
     * @throws Exception 需处理异常
     */
    public PublicKey getPublicKey(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(filename);
        DataInputStream dis = new DataInputStream(resourceAsStream);
        byte[] keyBytes = new byte[resourceAsStream.available()];
        dis.readFully(keyBytes);
        dis.close();
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance(KEY_ALGORITHM);
        return kf.generatePublic(spec);
    }

    /**
     * 从文件证书获取密钥
     *
     * @param filename 文件名
     * @return 密钥对象
     * @throws Exception 需处理异常
     */
    public PrivateKey getPrivateKey(String filename) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(filename);
        DataInputStream dis = new DataInputStream(resourceAsStream);
        byte[] keyBytes = new byte[resourceAsStream.available()];
        dis.readFully(keyBytes);
        dis.close();
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance(KEY_ALGORITHM);
        return kf.generatePrivate(spec);
    }

    /**
     * 通过公钥字节数据获取公钥
     *
     * @param publicKey 公钥字节数据
     * @return 公钥对象
     * @throws Exception 需处理异常
     */
    public PublicKey getPublicKey(byte[] publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKey);
        KeyFactory kf = KeyFactory.getInstance(KEY_ALGORITHM);
        return kf.generatePublic(spec);
    }

    /**
     * 通过密钥字节数据获取密钥
     *
     * @param privateKey 密钥字节数据
     * @return 密钥对象
     * @throws Exception 需处理异常
     */
    public PrivateKey getPrivateKey(byte[] privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKey);
        KeyFactory kf = KeyFactory.getInstance(KEY_ALGORITHM);
        return kf.generatePrivate(spec);
    }

    /**
     * 通过密钥和公钥文件证书生成rsa公钥和密钥
     *
     * @param publicKeyFilename
     * @param privateKeyFilename
     * @param password
     * @throws IOException
     * @throws NoSuchAlgorithmException
     */
    public void generateKey(String publicKeyFilename, String privateKeyFilename, String password) throws NoSuchAlgorithmException, FileNotFoundException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        SecureRandom secureRandom = new SecureRandom(password.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(publicKeyFilename);
            fos.write(publicKeyBytes);

            byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
            fos = new FileOutputStream(privateKeyFilename);
            fos.write(privateKeyBytes);
        } catch (IOException e) {
//            logger.error("生成密钥对错误：{}",e.getMessage());
            e.printStackTrace();
        }finally {
            IoUtil.close(fos);
        }
    }

    /**
     * 生存rsa公钥
     *
     * @param password 密码
     * @return 生成公钥字节数据
     * @throws NoSuchAlgorithmException
     */
    public static byte[] generatePublicKey(String password) throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        SecureRandom secureRandom = new SecureRandom(password.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        return keyPair.getPublic().getEncoded();
    }

    /**
     * 生存rsa密钥
     *
     * @param password 密码
     * @return 密钥字节数据
     * @throws NoSuchAlgorithmException
     */
    public static byte[] generatePrivateKey(String password) throws  NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        SecureRandom secureRandom = new SecureRandom(password.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        return keyPair.getPrivate().getEncoded();
    }

    /**
     * 通过密码生成公钥私钥
     * 
     * @param password 密码
     * @return 密钥对，为自己数据
     * @throws NoSuchAlgorithmException 需处理异常
     */
    public static Map<String, byte[]> generateKey(String password) throws IOException, NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        SecureRandom secureRandom = new SecureRandom(password.getBytes());
        //重新种植随机数，去除win 和Linux的生成差异
        secureRandom.setSeed(password.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        Map<String, byte[]> map = new HashMap<String, byte[]>();
        map.put("public", publicKeyBytes);
        map.put("private", privateKeyBytes);
        return map;
    }

    /**
     * 获取密钥对
     * 密钥对由byte转string
     * @param password 密码
     * @return 密钥对
     * @throws NoSuchAlgorithmException 算法异常
     */
    public static Map<String, String> generateKeysToString(String password) throws  NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        SecureRandom secureRandom = new SecureRandom(password.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        String publicKey = Base64.encodeBase64String(keyPair.getPublic().getEncoded());
        String privateKey = Base64.encodeBase64String(keyPair.getPrivate().getEncoded());
        Map<String,String> map = new HashMap<>();
        map.put("public", publicKey);
        map.put("private", privateKey);
        return map;
    }

    /**
     * 从生成的密钥对中获取公钥
     * @param map 已经生成的密钥对
     * @return 公钥
     */
    public static byte[] getPublicKey(Map<String, byte[]> map){
        return map.get("public");
    }

    /**
     * 从生成的密钥对中获取密钥
     * @param map 已经生成的密钥对
     * @return 密钥
     */
    public static byte[] getPrivateKey(Map<String, byte[]> map) {
        return map.get("private");
    }
    public static String toHexString(byte[] b) {
        return Base64.encodeBase64String(b);
    }

    public static final byte[] toBytes(String s) throws IOException {
        return Base64.decodeBase64(s);
    }
    
    /** 
	 * RSA私钥解密
	 *  
	 * @param str 加密字符串
	 * @param privateKey 私钥
	 * @return 铭文
	 * @throws Exception  解密过程中的异常信息
	 */  
	public static String decrypt(String str, byte[] privateKey) throws Exception {
        return new String(decryptByPrivateKey(Base64.decodeBase64(str.replaceAll("@", "+"))
                , Base64.encodeBase64String(privateKey), MAX_DECRYPT_1024));
	}

    private static byte[] decryptByPrivateKey(byte[] data, String privateKey,int length)
            throws Exception {
        byte[] keyBytes =  Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > length) {
                cache = cipher.doFinal(data, offSet, length);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * length;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    /**
     * 公钥加密
     * @param str 要加密的字符串
     * @param publicKey 公钥
     * @return
     * @throws Exception
     */
    public static String encrypt( String str, String publicKey) throws Exception{
        return Base64.encodeBase64String(encryptByPublicKey(str.getBytes(), publicKey, MAX_ENCRYPT_1024)).replaceAll("[+]", "@");
    }
    /**
     * 公钥分段加密
     * @param data 源数据
     * @param publicKey 公钥(BASE64编码)
     * @param length 段长
     * @return
     * @throws Exception
     */
    private static byte[] encryptByPublicKey(byte[] data, String publicKey,int length)
            throws Exception {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > length) {
                cache = cipher.doFinal(data, offSet, length);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * length;
        }
        byte[] encryptData = out.toByteArray();
        out.close();
        return encryptData;
    }


    public static void main(String[] args) throws NoSuchAlgorithmException, IOException, InvalidKeyException {
//        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
//        SecureRandom secureRandom = new SecureRandom("123".getBytes());
//        keyPairGenerator.initialize(1024, secureRandom);
//        //KeyPair keyPair = keyPairGenerator.genKeyPair();
//        //System.out.println(keyPair.getPublic().getEncoded());
//        Map<String, byte[]> ali = generateKey("ali");
//        System.out.println("public:"+ new String((ali.get("public"))));
//        System.out.println("测试公密钥生成是否一致");
        String password = "dddd";
        byte[] bytes = generatePublicKey(password);
        byte[] bytes1 = null;
        for (int i = 0; i < 50; i++) {
             bytes1 = generatePublicKey(password);
            String s = Base64.encodeBase64String(bytes);
//            System.out.println("1:["+s+"]");
            String s1 = Base64.encodeBase64String(bytes1);
//            System.out.println("1:["+s1+"]");
            System.out.println(s.equals(s1));
        }
    }

}
